Overview
In the previous assignment, we took the RNASeq data from the publication Cannabidiol inhibits SARS-COV-2 replication and promotes the host innate immune response by Nguyen et al., cleaned the data, performed normalization, gene identifier mapping, and some additional preliminary analysis on the dataset. As suggested by the title, the authors of the publication studied the effects of cannabidiol on SARS-CoV-2 replication and host’s immune response. The authors hypothesized and concluded that cannabidiol inhibits SARS-CoV-2 replication by up-regulating the host IRE1α ribonuclease endoplasmic reticulum (ER) stress response and interferon signaling pathways. The experiment conditions from in the dataset can be categorized into
- Infection with SARS-CoV-2, no treatment
- Infection with SARS-CoV-2, treatment with Cannabidiol
- No infection, no treatment
- No infection, treatment with Cannabidiol
with 3 replicates for each test condition.
We obtained the dataset from GEO with ID GSE168797, associated with the study Cannabidiol inhibits SARS-COV-2 replication and promotes the host innate immune response published on Science Advances. Out of the total of 57832 genes, 13705 remained after removing low counts and genes with duplicate identifiers.
Data normalization is performed using TMM with the edgeR package. Normalization corrects the large deviation of the means of untreated groups from the groups treated with CBD, while still preserving some of the characteristics of the original sample distribution.
counts_density_normalized <- apply(log2(normalized_counts), 2, density)
xlim <- 0; ylim <- 0
for (i in 1:length(counts_density_normalized)) {
xlim <- range(c(xlim, counts_density_normalized[[i]]$x));
ylim <- range(c(ylim, counts_density_normalized[[i]]$y))
}
cols <- rainbow(length(counts_density_normalized))
ltys <- rep(1, length(counts_density_normalized))
plot(counts_density_normalized[[1]], xlim = xlim, ylim = ylim, type = "n", ylab = "Smoothing density of log2-CPM", main = "Distribution of count density for each experiment condition (normalized)", cex.lab = 0.85)
for (i in 1:length(counts_density_normalized))
lines(counts_density_normalized[[i]], col = cols[i], lty = ltys[i])
legend("topright", colnames(data2plot), col=cols, lty=ltys, cex=0.75, border="blue", text.col = "green4", merge = TRUE, bg = "gray90")

We observe separation of groups after normalization.
limma::plotMDS(log2(normalized_counts), labels=rownames(samples), col = c("darkgreen","blue")[factor(samples$cbd_treatment)], main = "MDS plot after normalization showing distances between samples")
legend("topleft", legend=rownames(samples), fill=c("darkgreen","blue")[factor(samples$cbd_treatment)])

We will revisit this figure later when performing differential gene expression analysis.
Identifier mapping was performed using the package biomaRt with data from Ensembl. The Ensembl gene IDs in the original dataset are mapped to the corresponding HUGO gene symbols. The final dataset included 13705 genes with unique identifiers.
Differential Expression Analysis using edgeR
In this section, we are going to perform differential expression analysis.
Let us first examine the normalized data.
knitr::kable(d[1:10,1:6]$counts, caption = "Normalized gene counts") %>% kableExtra::kable_styling("striped")
Normalized gene counts
| |
CBD_infect-1 |
CBD_infect-2 |
CBD_infect-3 |
Veh_infect-1 |
Veh_infect-2 |
Veh_infect-3 |
| ENSG00000000003 |
1797 |
1912 |
2055 |
1210 |
1190 |
1139 |
| ENSG00000000419 |
2820 |
2834 |
3028 |
1984 |
1985 |
1981 |
| ENSG00000000457 |
615 |
518 |
526 |
819 |
777 |
845 |
| ENSG00000000460 |
439 |
523 |
553 |
779 |
781 |
849 |
| ENSG00000000971 |
982 |
1090 |
1024 |
1303 |
1030 |
1275 |
| ENSG00000001036 |
2699 |
3005 |
3054 |
1468 |
1412 |
1387 |
| ENSG00000001084 |
8435 |
8888 |
9079 |
4723 |
4449 |
4754 |
| ENSG00000001167 |
1178 |
1299 |
1388 |
1599 |
1796 |
1687 |
| ENSG00000001460 |
907 |
712 |
739 |
285 |
292 |
311 |
| ENSG00000001461 |
1325 |
1177 |
1161 |
551 |
517 |
429 |
knitr::kable(d[1:10,1:6]$samples, caption = "Sample groups") %>% kableExtra::kable_styling("striped")
Sample groups
| |
group |
lib.size |
norm.factors |
| CBD_infect-1 |
CBD |
38561370 |
1.4614028 |
| CBD_infect-2 |
CBD |
38330456 |
1.4411943 |
| CBD_infect-3 |
CBD |
39366212 |
1.4163787 |
| Veh_infect-1 |
Veh |
113140193 |
0.3877739 |
| Veh_infect-2 |
Veh |
116155068 |
0.3823093 |
| Veh_infect-3 |
Veh |
127114090 |
0.3454226 |
Workflow
We will be calculating differential expression following this general workflow:
Create deisng matrix. We will ensure that all factors that could contribute to differential expression is accounted for in the design matrix.
Use a mean-variance plot to confirm that the data follows the negative binomial distribution assumption for using the quasi-likelihood model in edgeR.
Fit our data to the model and calculated p-value and corrected p-value.
Use a threshold to extract differentially expressed genes with statistically significant differences.
Plot and visualize.
Creating the Design Matrix
In order to perform statistical testing, we need a design matrix the defines our model. Notice that in our dataset, there are three factors:
- Treatment with CBD
- Infection status (infected or not)
- Patient
Hence, ideally, we would like to account for all three factors in our design matrix.
model_design <- model.matrix(~ samples$patient + samples$cbd_treatment + samples$infected)
model_design[,4] <- !model_design[,4]
colnames(model_design)[4] <- "samples$cbd_treatmentCBD"
knitr::kable(model_design, caption = "Design matrix") %>%
kableExtra::kable_styling("striped")
Design matrix
| (Intercept) |
samples$patient2 |
samples$patient3 |
samples$cbd_treatmentCBD |
samples$infectedmock |
| 1 |
0 |
0 |
1 |
0 |
| 1 |
1 |
0 |
1 |
0 |
| 1 |
0 |
1 |
1 |
0 |
| 1 |
0 |
0 |
0 |
0 |
| 1 |
1 |
0 |
0 |
0 |
| 1 |
0 |
1 |
0 |
0 |
| 1 |
0 |
0 |
1 |
1 |
| 1 |
1 |
0 |
1 |
1 |
| 1 |
0 |
1 |
1 |
1 |
| 1 |
0 |
0 |
0 |
1 |
| 1 |
1 |
0 |
0 |
1 |
| 1 |
0 |
1 |
0 |
1 |
Distribution of Data
For our downstream analysis, we are going to use edgeR. We chose edgeR because it is specifically designed for RNASeq data. However, one important underlying assumption for using the quasi-likelihood model is that the data follows a negative binomial distribution. We need to verify that our dataset indeed meets that assumption.
Let us the calculate the dispersion and plot to visualize the mean-variance relationship.
d <- edgeR::DGEList(counts = normalized_counts_annot[,3:ncol(normalized_counts_annot)], group = samples$cbd_treatment)
d <- edgeR::estimateDisp(d, model_design)
edgeR::plotMeanVar(d,
show.raw.vars = TRUE,
show.tagwise.vars = TRUE,
NBline = TRUE,
show.ave.raw.vars = TRUE,
show.binned.common.disp.vars = TRUE,
main = "Mean-Variance Plot for Variance and Dispersion of Our Data")
# display legend
legend("topleft",
legend=c("Raw Data", "Tagwise Dispersion", "Average Raw Variances",
"Binned Common Dispersion", "Negative Binomial Line"),
col = c("grey", "lightblue", "maroon", "red", "dodgerblue2"), pch=c(1,1,4,4,NA), lty=c(0,0,0,0,1), lwd=c(1,1,1,1,2), cex=0.6)

As demonstrated by the mean-variance plot above, we can see that the dispersion and variance of our data indeed roughly follows the negative binomial distribution.
Analysis Using edgeR
Now, we have created the design matrix and verified the assumption for the data to be negative-binomially distributed, we can proceed to the next stage of our analysis and perform statistical testing and corrections to ensure that we only get significantly differentially expressed genes. We used the quasi-likelihood models since our dataset is from an RNASeq experiment and quasi-likelihood models are best suited to handle RNASeq data.
fit <- edgeR::glmQLFit(d, model_design)
Once we have fit the model, we can proceed to calculate differential expression. Recall that our goal is to verify the role of cannabidiol in affecting the replication ability of SARS-CoV-2, so we will be using cbd_treatment as the contrast.
qlf <- edgeR::glmQLFTest(fit, coef = 'samples$cbd_treatmentCBD')
qlf_diff_exp <- edgeR::topTags(qlf, sort.by = "PValue", n = nrow(normalized_counts_annot))
knitr::kable(qlf_diff_exp[1:10,]$table, type="html", digits = 20, caption = "Top differentially expressed genes") %>%
kableExtra::kable_styling("striped")
Top differentially expressed genes
| |
logFC |
logCPM |
F |
PValue |
FDR |
| DNAJB9 |
3.296477 |
6.632298 |
2376.139 |
1.174076e-13 |
1.225637e-09 |
| IGFBP1 |
4.315555 |
7.225448 |
2191.283 |
1.788598e-13 |
1.225637e-09 |
| HSPA5 |
3.721184 |
11.833327 |
1920.096 |
3.552926e-13 |
1.623095e-09 |
| CRELD2 |
3.023632 |
6.669438 |
1395.860 |
1.858463e-12 |
5.742205e-09 |
| KIF20A |
-2.327554 |
5.891384 |
1344.930 |
2.253309e-12 |
5.742205e-09 |
| ADGRF4 |
2.315894 |
4.618755 |
1287.278 |
2.827571e-12 |
5.742205e-09 |
| GABARAPL1 |
2.351451 |
6.414219 |
1278.222 |
2.932903e-12 |
5.742205e-09 |
| NDRG4 |
1.776359 |
6.129025 |
1133.516 |
5.463712e-12 |
9.159587e-09 |
| HERPUD1 |
3.400467 |
8.195296 |
1061.159 |
7.686235e-12 |
9.159587e-09 |
| STC2 |
2.742212 |
6.444858 |
1059.247 |
7.758262e-12 |
9.159587e-09 |
We can examine the number of genes pass the threshold and correction. We are using 0.05 as the threshold for p-value as it is commonly used in practice.
# number of genes that passed the threshold
sum(qlf_diff_exp$table$PValue < 0.05)
[1] 5501
# number of genes that passed correction
sum(qlf_diff_exp$table$FDR < 0.05)
[1] 4424
The threshold of 0.05 gives us quite a lot of genes, in order to get more meaningful hits in the downstream gene enrichment analysis, we will make the thershold more stringent.
# number of genes that passed the threshold
sum(qlf_diff_exp$table$PValue < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5)
[1] 513
# number of genes that passed correction
sum(qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5)
[1] 442
Visualization of Differentially Expressed Genes
Now we can retrieve the list of differential expressed genes and visualize using different plots. We will first plot them on a volcano plot. Each gene is represented by a point in the plot. The horizontal axis of the plot is the \(\log_2\) fold change and the vertical axis is the \(-\log_{10}p\) which indicates the statistical significance of each gene (how likely the differential is due to actual biological variation).
volcano_color_palette = rep('gray', times = nrow(qlf_diff_exp$table))
volcano_color_palette[qlf_diff_exp$table$logFC < 0 & qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5] <- 'blue'
volcano_color_palette[qlf_diff_exp$table$logFC > 0 & qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5] <- 'red'
plot(qlf_diff_exp$table$logFC,
-log(qlf_diff_exp$table$PValue, base=10),
col = volcano_color_palette,
xlab = "log2 fold change",
ylab = "-log10 p",
main = "Volcano plot showing upregulated and downregulated genes"
)
legend("topright", legend=c("Downregulated in CBD treated cells","Upregulated in CBD treated cells", "Not significant"),fill = c("blue", "red", "grey"), cex = 0.5)

Next, we will visualize the upregulated and downregulated genes across different test conditions using a heatmap.
diff_exp_lst <- qlf_diff_exp$table[qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC),]
diff_exp_lst$hgnc_symbol <- rownames(diff_exp_lst)
# normalized counts of differentially expressed genes
mat_count_normalized_diff <- normalized_counts_annot[diff_exp_lst$hgnc_symbol, 3:ncol(normalized_counts_annot)]
# rescale the counts
mat_count_normalized_diff <- t(scale(t(mat_count_normalized_diff)))
heatmap_color_palette <- circlize::colorRamp2(c(min(mat_count_normalized_diff), 0, max(mat_count_normalized_diff)),
c("blue", "white", "red"))
ComplexHeatmap::Heatmap(as.matrix(mat_count_normalized_diff),
name = "scaled normalized count",
cluster_rows = TRUE,
cluster_columns = TRUE,
show_row_dend = FALSE,
show_column_dend = FALSE,
col = heatmap_color_palette,
show_column_names = TRUE,
show_row_names = FALSE,
show_heatmap_legend = TRUE,
column_title = "Samples",
row_title = "Genes",
use_raster = TRUE)

Discussion
Initially, I used the p-value of 0.05 as it is widely used in practice. This gives us 5501 genes prior to correction. This is quite a large number of genes, so we changed the p-value threshold to 0.01 to limit the number of genes included. We further added the criteria that a gene must have an absolute log fold change greater than 1.5. By doing so, we are contraining ourselves to only get the genes that are highly differentially expressed with high statistical significance.
For correction, we used Benjamini-Hochberg. The two main methods discussed for correcting family-wise false discovery rate are Bonferroni and Benjamini-Hochberg correction. We want to get meaningful hits without excluding statistically significant ones. Bonferroni’s method is overly stringent so it is not quite suitable for our purposes. Using Benjamini-Hochberg will give us a richer set of genes that we can use for downstream analysis.
Volcano plot shown above. Note that we also applied the restriction that only genes with aboslute log fold change greater than 1.5 are to be included.
There is significant clustering within conditions. This suggests that the test condition (CBD v.s. no CBD) does have a significant effect on gene expressions.
Thresholded Overrepresentation Analysis using g:Profiler
For the final part of this assignment, we will perform a thresholded overrepresentation analysis using g:Profiler. In the previous section, we have compiled a list of differentially expressed genes. Here, we want to further divide them into upregulated and downregulated genes.
upregulated_gene_lst <- diff_exp_lst[diff_exp_lst$logFC > 0,]
downregulated_gene_lst <- diff_exp_lst[diff_exp_lst$logFC < 0,]
We use the R package for g:Profiler to perform the gene enrichment analysis. For correction, we used FDR as it is less stringent than Bonferroni and is introduced as the preferred correction method in class. We used GO Biological Process, GO Molecular Function, and KEGG as those are the ones used by the author of the original publication. For a more detailed overview of the workflow, please refer to the Discussion subsection located at the end of this section.
Upregulated Genes
up_top_terms_all <- gprofiler2::gost(query = rownames(upregulated_gene_lst),
organism = "hsapiens",
exclude_iea = TRUE,
correction_method = "fdr",
sources = c("GO:BP", "GO:MF", "KEGG"))
up_top_terms <- data.frame(
term_name = up_top_terms_all$result$term_name[up_top_terms_all$result$term_size < 500 &
up_top_terms_all$result$term_size > 2],
term_id = up_top_terms_all$result$term_id[up_top_terms_all$result$term_size < 500 &
up_top_terms_all$result$term_size > 2],
source = up_top_terms_all$result$source[up_top_terms_all$result$term_size < 500 &
up_top_terms_all$result$term_size > 2]
)
knitr::kable(up_top_terms[1:10,], caption = "Top genesets using list of upregulated genes") %>% kableExtra::kable_styling("striped")
Top genesets using list of upregulated genes
| term_name |
term_id |
source |
| response to endoplasmic reticulum stress |
GO:0034976 |
GO:BP |
| proteasomal protein catabolic process |
GO:0010498 |
GO:BP |
| ERAD pathway |
GO:0036503 |
GO:BP |
| ubiquitin-dependent ERAD pathway |
GO:0030433 |
GO:BP |
| proteasome-mediated ubiquitin-dependent protein catabolic process |
GO:0043161 |
GO:BP |
| autophagy |
GO:0006914 |
GO:BP |
| process utilizing autophagic mechanism |
GO:0061919 |
GO:BP |
| response to topologically incorrect protein |
GO:0035966 |
GO:BP |
| macroautophagy |
GO:0016236 |
GO:BP |
| endoplasmic reticulum to Golgi vesicle-mediated transport |
GO:0006888 |
GO:BP |
For context, let’s examine the top term from each data source.
knitr::kable(rbind(up_top_terms[up_top_terms$source == "GO:BP",][1,],
up_top_terms[up_top_terms$source == "GO:MF",][1,],
up_top_terms[up_top_terms$source == "KEGG",][1,]),
caption = "Top terms from each data source using list of upregulated genes") %>%
kableExtra::kable_styling("striped")
Top terms from each data source using list of upregulated genes
| |
term_name |
term_id |
source |
| 1 |
response to endoplasmic reticulum stress |
GO:0034976 |
GO:BP |
| 331 |
unfolded protein binding |
GO:0051082 |
GO:MF |
| 401 |
Protein processing in endoplasmic reticulum |
KEGG:04141 |
KEGG |
We can visualize the distribution of top terms from each data source using an Manhattan plot. This is the distribution of terms prior to removing large terms with over 500 genes.
gprofiler2::gostplot(up_top_terms_all) %>% plotly::layout(title = "Manhattan plot showing distribution of terms \nfrom each data source using list of upregulated genes", font = list(size = 10))
length(up_top_terms$term_name)
[1] 433
Downregulated Genes
We do the same for the downregualted genes.
down_top_terms_all <- gprofiler2::gost(query = rownames(downregulated_gene_lst),
organism = "hsapiens",
exclude_iea = TRUE,
correction_method = "fdr",
sources = c("GO:BP", "GO:MF", "KEGG"))
down_top_terms <- data.frame(
term_name = down_top_terms_all$result$term_name[down_top_terms_all$result$term_size < 500 &
down_top_terms_all$result$term_size > 2],
term_id = down_top_terms_all$result$term_id[down_top_terms_all$result$term_size < 500 &
down_top_terms_all$result$term_size > 2],
source = down_top_terms_all$result$source[down_top_terms_all$result$term_size < 500 &
down_top_terms_all$result$term_size > 2]
)
knitr::kable(down_top_terms[1:10,], caption = "Top genesets using list of downregulated genes") %>% kableExtra::kable_styling("striped")
Top genesets using list of downregulated genes
| term_name |
term_id |
source |
| mitotic nuclear division |
GO:0140014 |
GO:BP |
| sister chromatid segregation |
GO:0000819 |
GO:BP |
| mitotic sister chromatid segregation |
GO:0000070 |
GO:BP |
| nuclear division |
GO:0000280 |
GO:BP |
| chromosome segregation |
GO:0007059 |
GO:BP |
| organelle fission |
GO:0048285 |
GO:BP |
| nuclear chromosome segregation |
GO:0098813 |
GO:BP |
| cell cycle phase transition |
GO:0044770 |
GO:BP |
| regulation of cell cycle phase transition |
GO:1901987 |
GO:BP |
| DNA replication |
GO:0006260 |
GO:BP |
knitr::kable(rbind(down_top_terms[down_top_terms$source == "GO:BP",][1,],
down_top_terms[down_top_terms$source == "GO:MF",][1,],
down_top_terms[down_top_terms$source == "KEGG",][1,]),
caption = "Top terms from each data source using list of downregulated genes") %>%
kableExtra::kable_styling("striped")
Top terms from each data source using list of downregulated genes
| |
term_name |
term_id |
source |
| 1 |
mitotic nuclear division |
GO:0140014 |
GO:BP |
| 474 |
chromatin binding |
GO:0003682 |
GO:MF |
| 535 |
Cell cycle |
KEGG:04110 |
KEGG |
Plot the Manhattan plot showing distribution of terms from each data source using list of downregulated genes.
gprofiler2::gostplot(down_top_terms_all) %>% plotly::layout(title = "Manhattan plot showing distribution of terms\n from each data source using list of downregulated genes", font = list(size = 10))
length(down_top_terms$term_name)
[1] 560
All Differentially Expressed Genes
Finally, for all differentially expressed genes.
top_terms_all <- gprofiler2::gost(query = rownames(diff_exp_lst),
organism = "hsapiens",
exclude_iea = TRUE,
correction_method = "fdr",
sources = c("GO:BP", "GO:MF", "KEGG"))
top_terms <- data.frame(
term_name = top_terms_all$result$term_name[top_terms_all$result$term_size < 500 &
top_terms_all$result$term_size > 2],
term_id = top_terms_all$result$term_id[top_terms_all$result$term_size < 500 &
top_terms_all$result$term_size > 2],
source = top_terms_all$result$source[top_terms_all$result$term_size < 500 &
top_terms_all$result$term_size > 2]
)
knitr::kable(top_terms[1:10,], caption = "Top genesets using list of all differentially expressed genes") %>% kableExtra::kable_styling("striped")
Top genesets using list of all differentially expressed genes
| term_name |
term_id |
source |
| mitotic sister chromatid segregation |
GO:0000070 |
GO:BP |
| mitotic nuclear division |
GO:0140014 |
GO:BP |
| sister chromatid segregation |
GO:0000819 |
GO:BP |
| response to endoplasmic reticulum stress |
GO:0034976 |
GO:BP |
| proteasomal protein catabolic process |
GO:0010498 |
GO:BP |
| organelle fission |
GO:0048285 |
GO:BP |
| chromosome segregation |
GO:0007059 |
GO:BP |
| nuclear chromosome segregation |
GO:0098813 |
GO:BP |
| nuclear division |
GO:0000280 |
GO:BP |
| regulation of cell cycle phase transition |
GO:1901987 |
GO:BP |
knitr::kable(rbind(top_terms[top_terms$source == "GO:BP",][1,],
top_terms[top_terms$source == "GO:MF",][1,],
top_terms[top_terms$source == "KEGG",][1,]),
caption = "Top terms from each data source using list of all differentially expressed genes") %>%
kableExtra::kable_styling("striped")
Top terms from each data source using list of all differentially expressed genes
| |
term_name |
term_id |
source |
| 1 |
mitotic sister chromatid segregation |
GO:0000070 |
GO:BP |
| 514 |
cadherin binding |
GO:0045296 |
GO:MF |
| 583 |
Protein processing in endoplasmic reticulum |
KEGG:04141 |
KEGG |
gprofiler2::gostplot(top_terms_all) %>% plotly::layout(title = "Manhattan plot showing distribution of terms from each data source", font = list(size = 10))
length(top_terms$term_name)
[1] 616
Discussion
We used g:Profiler as we have extensively discussed about it in class. It has a nice web-based interface as well as easy-to-use APIs in the form of an R package. Furthermore, the data sources on g:Profiler is frequently updated and include the data sources that we are most interested in.
We used GO Biological Process, GO Molecular Function, and KEGO. We chose these data sources since they are also used by the author of the original paper from which I obtained the dataset. However, since KEGO is a commercial data source, if it was not for that fact that the original paper used it, I would personally refrain from using it. The original author also used other data sources including Canonical Pathways but since it is not part of g:Profiler, we did not include these data sources. The annotation source versions are as follows: Ensembl 105, Ensembl Genomes 52 (database built on 2022-02-14)
For all three analysis (using upregulated, downregulated, all differentially expressed), we used a threshold between 2 and 500. We set the upper bound to 500 because we do not want to include overly broad and generic terms that will not give us meaningful insights into the roles of the differentially expressed genes. The set of upregulated genes returned 433 genesets; the set of downregulated genes returned 560 genesets; the set of all differentially expressed genes returned 616 gene sets.
The result using the whole list (set of all differentially expressed genes) is more predominantly represented by the downregulated genes. It also does not provide a lot of insights into the roles of the genes as it is a mix of drastically different and seemingly unrelated terms (e.g. mitotic nuclear division, organelle fission, response to endoplasmic reticulum stress, etc.). However, by using separating into upregualted and downregulated genes and performing the gene enrichment analysis separately, we get more meaningful terms: the upregualted genes are mostly associated with pathways and processes involving endoplasmic reticulum whereas the downregulated genes are mostly associated with the cell cycle.
LS0tCnRpdGxlOiAiS2V2aW4gR2FvIC0gQXNzaWdubWVudCAyOiBEaWZmZXJlbnRpYWwgR2VuZSBleHByZXNzaW9uIGFuZCBQcmVsaW1pbmFyeSBPUkEiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICBtYXRoamF4OiAiaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvbWF0aGpheC8yLjcuNy9NYXRoSmF4LmpzP2NvbmZpZz1UZVgtTU1MLUFNX0NIVE1MIgotLS0KCiMgT3ZlcnZpZXcKCmBgYHtyIGExX3NvdXJjZSwgaW5jbHVkZT1GQUxTRSxlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpvcHRpb25zKHdhcm49LTEpCmExX291dHB1dCA8LSBrbml0cjo6a25pdF9jaGlsZCgnYXNzaWdubWVudDEuUm1kJywgcXVpZXQgPSBUUlVFKQpgYGAKCkluIHRoZSBwcmV2aW91cyBhc3NpZ25tZW50LCB3ZSB0b29rIHRoZSBSTkFTZXEgZGF0YSBmcm9tIHRoZSBwdWJsaWNhdGlvbiAqQ2FubmFiaWRpb2wgaW5oaWJpdHMgU0FSUy1DT1YtMiByZXBsaWNhdGlvbiBhbmQgcHJvbW90ZXMgdGhlIGhvc3QgaW5uYXRlIGltbXVuZSByZXNwb25zZSogYnkgTmd1eWVuIGV0IGFsLiwgY2xlYW5lZCB0aGUgZGF0YSwgcGVyZm9ybWVkIG5vcm1hbGl6YXRpb24sIGdlbmUgaWRlbnRpZmllciBtYXBwaW5nLCBhbmQgc29tZSBhZGRpdGlvbmFsIHByZWxpbWluYXJ5IGFuYWx5c2lzIG9uIHRoZSBkYXRhc2V0LiBBcyBzdWdnZXN0ZWQgYnkgdGhlIHRpdGxlLCB0aGUgYXV0aG9ycyBvZiB0aGUgcHVibGljYXRpb24gc3R1ZGllZCB0aGUgZWZmZWN0cyBvZiBjYW5uYWJpZGlvbCBvbiBTQVJTLUNvVi0yIHJlcGxpY2F0aW9uIGFuZCBob3N0J3MgaW1tdW5lIHJlc3BvbnNlLiBUaGUgYXV0aG9ycyBoeXBvdGhlc2l6ZWQgYW5kIGNvbmNsdWRlZCB0aGF0IGNhbm5hYmlkaW9sIGluaGliaXRzIFNBUlMtQ29WLTIgcmVwbGljYXRpb24gYnkgdXAtcmVndWxhdGluZyB0aGUgaG9zdCBJUkUxzrEgcmlib251Y2xlYXNlIGVuZG9wbGFzbWljIHJldGljdWx1bSAoRVIpIHN0cmVzcyByZXNwb25zZSBhbmQgaW50ZXJmZXJvbiBzaWduYWxpbmcgcGF0aHdheXMuIFRoZSBleHBlcmltZW50IGNvbmRpdGlvbnMgZnJvbSBpbiB0aGUgZGF0YXNldCBjYW4gYmUgY2F0ZWdvcml6ZWQgaW50bwoKLSBJbmZlY3Rpb24gd2l0aCBTQVJTLUNvVi0yLCBubyB0cmVhdG1lbnQKLSBJbmZlY3Rpb24gd2l0aCBTQVJTLUNvVi0yLCB0cmVhdG1lbnQgd2l0aCBDYW5uYWJpZGlvbAotIE5vIGluZmVjdGlvbiwgbm8gdHJlYXRtZW50Ci0gTm8gaW5mZWN0aW9uLCB0cmVhdG1lbnQgd2l0aCBDYW5uYWJpZGlvbAoKd2l0aCAzIHJlcGxpY2F0ZXMgZm9yIGVhY2ggdGVzdCBjb25kaXRpb24uCgpXZSBvYnRhaW5lZCB0aGUgZGF0YXNldCBmcm9tIEdFTyB3aXRoIElEIEdTRTE2ODc5NywgYXNzb2NpYXRlZCB3aXRoIHRoZSBzdHVkeSAqQ2FubmFiaWRpb2wgaW5oaWJpdHMgU0FSUy1DT1YtMiByZXBsaWNhdGlvbiBhbmQgcHJvbW90ZXMgdGhlIGhvc3QgaW5uYXRlIGltbXVuZSByZXNwb25zZSogcHVibGlzaGVkIG9uIFNjaWVuY2UgQWR2YW5jZXMuIE91dCBvZiB0aGUgdG90YWwgb2YgNTc4MzIgZ2VuZXMsIDEzNzA1IHJlbWFpbmVkIGFmdGVyIHJlbW92aW5nIGxvdyBjb3VudHMgYW5kIGdlbmVzIHdpdGggZHVwbGljYXRlIGlkZW50aWZpZXJzLgoKRGF0YSBub3JtYWxpemF0aW9uIGlzIHBlcmZvcm1lZCB1c2luZyBUTU0gd2l0aCB0aGUgZWRnZVIgcGFja2FnZS4gTm9ybWFsaXphdGlvbiBjb3JyZWN0cyB0aGUgbGFyZ2UgZGV2aWF0aW9uIG9mIHRoZSBtZWFucyBvZiB1bnRyZWF0ZWQgZ3JvdXBzIGZyb20gdGhlIGdyb3VwcyB0cmVhdGVkIHdpdGggQ0JELCB3aGlsZSBzdGlsbCBwcmVzZXJ2aW5nIHNvbWUgb2YgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgb3JpZ2luYWwgc2FtcGxlIGRpc3RyaWJ1dGlvbi4KCmBgYHtyIG5vcm1hbGl6ZWRfZGF0YV9wbG90LCBtZXNzYWdlPUZBTFNFfQpjb3VudHNfZGVuc2l0eV9ub3JtYWxpemVkIDwtIGFwcGx5KGxvZzIobm9ybWFsaXplZF9jb3VudHMpLCAyLCBkZW5zaXR5KQoKeGxpbSA8LSAwOyB5bGltIDwtIDAKZm9yIChpIGluIDE6bGVuZ3RoKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWQpKSB7CiAgeGxpbSA8LSByYW5nZShjKHhsaW0sIGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbW2ldXSR4KSk7CiAgeWxpbSA8LSByYW5nZShjKHlsaW0sIGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbW2ldXSR5KSkKfQpjb2xzIDwtIHJhaW5ib3cobGVuZ3RoKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWQpKQpsdHlzIDwtIHJlcCgxLCBsZW5ndGgoY291bnRzX2RlbnNpdHlfbm9ybWFsaXplZCkpCgpwbG90KGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbWzFdXSwgeGxpbSA9IHhsaW0sIHlsaW0gPSB5bGltLCB0eXBlID0gIm4iLCB5bGFiID0gIlNtb290aGluZyBkZW5zaXR5IG9mIGxvZzItQ1BNIiwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgY291bnQgZGVuc2l0eSBmb3IgZWFjaCBleHBlcmltZW50IGNvbmRpdGlvbiAobm9ybWFsaXplZCkiLCBjZXgubGFiID0gMC44NSkKZm9yIChpIGluIDE6bGVuZ3RoKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWQpKQogIGxpbmVzKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbW2ldXSwgY29sID0gY29sc1tpXSwgbHR5ID0gbHR5c1tpXSkKbGVnZW5kKCJ0b3ByaWdodCIsIGNvbG5hbWVzKGRhdGEycGxvdCksIGNvbD1jb2xzLCBsdHk9bHR5cywgY2V4PTAuNzUsIGJvcmRlcj0iYmx1ZSIsIHRleHQuY29sID0gImdyZWVuNCIsIG1lcmdlID0gVFJVRSwgYmcgPSAiZ3JheTkwIikKYGBgCgpXZSBvYnNlcnZlIHNlcGFyYXRpb24gb2YgZ3JvdXBzIGFmdGVyIG5vcm1hbGl6YXRpb24uCgpgYGB7ciBhMV9tZHMsIG1lc3NhZ2U9RkFMU0V9CmxpbW1hOjpwbG90TURTKGxvZzIobm9ybWFsaXplZF9jb3VudHMpLCBsYWJlbHM9cm93bmFtZXMoc2FtcGxlcyksIGNvbCA9IGMoImRhcmtncmVlbiIsImJsdWUiKVtmYWN0b3Ioc2FtcGxlcyRjYmRfdHJlYXRtZW50KV0sIG1haW4gPSAiTURTIHBsb3QgYWZ0ZXIgbm9ybWFsaXphdGlvbiBzaG93aW5nIGRpc3RhbmNlcyBiZXR3ZWVuIHNhbXBsZXMiKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9cm93bmFtZXMoc2FtcGxlcyksIGZpbGw9YygiZGFya2dyZWVuIiwiYmx1ZSIpW2ZhY3RvcihzYW1wbGVzJGNiZF90cmVhdG1lbnQpXSkKYGBgCgpXZSB3aWxsIHJldmlzaXQgdGhpcyBmaWd1cmUgbGF0ZXIgd2hlbiBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuCgpJZGVudGlmaWVyIG1hcHBpbmcgd2FzIHBlcmZvcm1lZCB1c2luZyB0aGUgcGFja2FnZSBiaW9tYVJ0IHdpdGggZGF0YSBmcm9tIEVuc2VtYmwuIFRoZSBFbnNlbWJsIGdlbmUgSURzIGluIHRoZSBvcmlnaW5hbCBkYXRhc2V0IGFyZSBtYXBwZWQgdG8gdGhlIGNvcnJlc3BvbmRpbmcgSFVHTyBnZW5lIHN5bWJvbHMuIFRoZSBmaW5hbCBkYXRhc2V0IGluY2x1ZGVkIDEzNzA1IGdlbmVzIHdpdGggdW5pcXVlIGlkZW50aWZpZXJzLgoKIyBTZXR1cAoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBpbXBvcnQgYW5kIGluc3RhbGwgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyBmb3IgdGhpcyBhc3NpZ25tZW50LCBpbiB3aGljaCB3ZSB3aWxsIGNvbmR1Y3QgYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyB1c2luZyB0aGUgbm9ybWFsaXplZCBkYXRhc2V0IGFuZCBhIHRocmVzaG9sZGVkIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMuCgpgYGB7ciBhMl9wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJtYWdyaXR0ciIpKQogIGluc3RhbGwucGFja2FnZXMoIm1hZ3JpdHRyIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJjaXJjbGl6ZSIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoImNpcmNsaXplIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJDb21wbGV4SGVhdG1hcCIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJDb21wbGV4SGVhdG1hcCIpCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiZ3Byb2ZpbGVyMiIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJncHJvZmlsZXIyIikKCiMgZWRnZVIgYW5kIG90aGVyIHBhY2thZ2VzIGFyZSBpbXBvcnRlZCBpbiBBMSwgd2hpY2ggaXMgc291cmNlZCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBub3RlYm9vawoKbGlicmFyeShtYWdyaXR0cikKCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UpCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpcyB1c2luZyBlZGdlUgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBhcmUgZ29pbmcgdG8gcGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCkxldCB1cyBmaXJzdCBleGFtaW5lIHRoZSBub3JtYWxpemVkIGRhdGEuCgpgYGB7ciBub3JtYWxpemVkX2RhdGEsIG1lc3NhZ2U9RkFMU0V9CmtuaXRyOjprYWJsZShkWzE6MTAsMTo2XSRjb3VudHMsIGNhcHRpb24gPSAiTm9ybWFsaXplZCBnZW5lIGNvdW50cyIpICU+JSBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikKa25pdHI6OmthYmxlKGRbMToxMCwxOjZdJHNhbXBsZXMsIGNhcHRpb24gPSAiU2FtcGxlIGdyb3VwcyIpICU+JSBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikKYGBgCiMjIFdvcmtmbG93CgpXZSB3aWxsIGJlIGNhbGN1bGF0aW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGZvbGxvd2luZyB0aGlzIGdlbmVyYWwgd29ya2Zsb3c6CgoxLiBDcmVhdGUgZGVpc25nIG1hdHJpeC4gV2Ugd2lsbCBlbnN1cmUgdGhhdCBhbGwgZmFjdG9ycyB0aGF0IGNvdWxkIGNvbnRyaWJ1dGUgdG8gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaXMgYWNjb3VudGVkIGZvciBpbiB0aGUgZGVzaWduIG1hdHJpeC4KCjIuIFVzZSBhIG1lYW4tdmFyaWFuY2UgcGxvdCB0byBjb25maXJtIHRoYXQgdGhlIGRhdGEgZm9sbG93cyB0aGUgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uIGFzc3VtcHRpb24gZm9yIHVzaW5nIHRoZSBxdWFzaS1saWtlbGlob29kIG1vZGVsIGluIGVkZ2VSLgoKMy4gRml0IG91ciBkYXRhIHRvIHRoZSBtb2RlbCBhbmQgY2FsY3VsYXRlZCBwLXZhbHVlIGFuZCBjb3JyZWN0ZWQgcC12YWx1ZS4KCjQuIFVzZSBhIHRocmVzaG9sZCB0byBleHRyYWN0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyB3aXRoIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMuCgo1LiBQbG90IGFuZCB2aXN1YWxpemUuCgojIyBDcmVhdGluZyB0aGUgRGVzaWduIE1hdHJpeAoKSW4gb3JkZXIgdG8gcGVyZm9ybSBzdGF0aXN0aWNhbCB0ZXN0aW5nLCB3ZSBuZWVkIGEgZGVzaWduIG1hdHJpeCB0aGUgZGVmaW5lcyBvdXIgbW9kZWwuIE5vdGljZSB0aGF0IGluIG91ciBkYXRhc2V0LCB0aGVyZSBhcmUgdGhyZWUgZmFjdG9yczogCgoxLiBUcmVhdG1lbnQgd2l0aCBDQkQKMi4gSW5mZWN0aW9uIHN0YXR1cyAoaW5mZWN0ZWQgb3Igbm90KQozLiBQYXRpZW50CgpIZW5jZSwgaWRlYWxseSwgd2Ugd291bGQgbGlrZSB0byBhY2NvdW50IGZvciBhbGwgdGhyZWUgZmFjdG9ycyBpbiBvdXIgZGVzaWduIG1hdHJpeC4KCmBgYHtyIGRlc2lnbl9tYXRyaXgsIG1lc3NhZ2U9RkFMU0V9Cm1vZGVsX2Rlc2lnbiA8LSBtb2RlbC5tYXRyaXgofiBzYW1wbGVzJHBhdGllbnQgKyBzYW1wbGVzJGNiZF90cmVhdG1lbnQgKyBzYW1wbGVzJGluZmVjdGVkKQptb2RlbF9kZXNpZ25bLDRdIDwtICFtb2RlbF9kZXNpZ25bLDRdCmNvbG5hbWVzKG1vZGVsX2Rlc2lnbilbNF0gPC0gInNhbXBsZXMkY2JkX3RyZWF0bWVudENCRCIKa25pdHI6OmthYmxlKG1vZGVsX2Rlc2lnbiwgY2FwdGlvbiA9ICJEZXNpZ24gbWF0cml4IikgJT4lCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYAoKIyMgRGlzdHJpYnV0aW9uIG9mIERhdGEKCkZvciBvdXIgZG93bnN0cmVhbSBhbmFseXNpcywgd2UgYXJlIGdvaW5nIHRvIHVzZSBlZGdlUi4gV2UgY2hvc2UgZWRnZVIgYmVjYXVzZSBpdCBpcyBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIFJOQVNlcSBkYXRhLiBIb3dldmVyLCBvbmUgaW1wb3J0YW50IHVuZGVybHlpbmcgYXNzdW1wdGlvbiBmb3IgdXNpbmcgdGhlIHF1YXNpLWxpa2VsaWhvb2QgbW9kZWwgaXMgdGhhdCB0aGUgZGF0YSBmb2xsb3dzIGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uLiBXZSBuZWVkIHRvIHZlcmlmeSB0aGF0IG91ciBkYXRhc2V0IGluZGVlZCBtZWV0cyB0aGF0IGFzc3VtcHRpb24uCgpMZXQgdXMgdGhlIGNhbGN1bGF0ZSB0aGUgZGlzcGVyc2lvbiBhbmQgcGxvdCB0byB2aXN1YWxpemUgdGhlIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwLgoKYGBge3IgZGlzcGVyc2lvbl9jYWxjLCBtZXNzYWdlPUZBTFNFfQpkIDwtIGVkZ2VSOjpER0VMaXN0KGNvdW50cyA9IG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WywzOm5jb2wobm9ybWFsaXplZF9jb3VudHNfYW5ub3QpXSwgZ3JvdXAgPSBzYW1wbGVzJGNiZF90cmVhdG1lbnQpCmQgPC0gZWRnZVI6OmVzdGltYXRlRGlzcChkLCBtb2RlbF9kZXNpZ24pCmBgYAoKYGBge3IgbWVhbl92YXJfcGxvdH0KZWRnZVI6OnBsb3RNZWFuVmFyKGQsCiAgICAgICAgICAgICAgICAgICBzaG93LnJhdy52YXJzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIHNob3cudGFnd2lzZS52YXJzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIE5CbGluZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBzaG93LmF2ZS5yYXcudmFycyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBzaG93LmJpbm5lZC5jb21tb24uZGlzcC52YXJzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiTWVhbi1WYXJpYW5jZSBQbG90IGZvciBWYXJpYW5jZSBhbmQgRGlzcGVyc2lvbiBvZiBPdXIgRGF0YSIpCiMgZGlzcGxheSBsZWdlbmQKbGVnZW5kKCJ0b3BsZWZ0IiwgCiAgICAgICBsZWdlbmQ9YygiUmF3IERhdGEiLCAiVGFnd2lzZSBEaXNwZXJzaW9uIiwgIkF2ZXJhZ2UgUmF3IFZhcmlhbmNlcyIsIAogICAgICAgICAgICAgICAgIkJpbm5lZCBDb21tb24gRGlzcGVyc2lvbiIsICJOZWdhdGl2ZSBCaW5vbWlhbCBMaW5lIiksIAogICAgICAgY29sID0gYygiZ3JleSIsICJsaWdodGJsdWUiLCAibWFyb29uIiwgInJlZCIsICJkb2RnZXJibHVlMiIpLCBwY2g9YygxLDEsNCw0LE5BKSwgbHR5PWMoMCwwLDAsMCwxKSwgbHdkPWMoMSwxLDEsMSwyKSwgY2V4PTAuNikKYGBgCgpBcyBkZW1vbnN0cmF0ZWQgYnkgdGhlIG1lYW4tdmFyaWFuY2UgcGxvdCBhYm92ZSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBkaXNwZXJzaW9uIGFuZCB2YXJpYW5jZSBvZiBvdXIgZGF0YSBpbmRlZWQgcm91Z2hseSBmb2xsb3dzIHRoZSBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24uCgojIyBBbmFseXNpcyBVc2luZyBlZGdlUgoKTm93LCB3ZSBoYXZlIGNyZWF0ZWQgdGhlIGRlc2lnbiBtYXRyaXggYW5kIHZlcmlmaWVkIHRoZSBhc3N1bXB0aW9uIGZvciB0aGUgZGF0YSB0byBiZSBuZWdhdGl2ZS1iaW5vbWlhbGx5IGRpc3RyaWJ1dGVkLCB3ZSBjYW4gcHJvY2VlZCB0byB0aGUgbmV4dCBzdGFnZSBvZiBvdXIgYW5hbHlzaXMgYW5kIHBlcmZvcm0gc3RhdGlzdGljYWwgdGVzdGluZyBhbmQgY29ycmVjdGlvbnMgdG8gZW5zdXJlIHRoYXQgd2Ugb25seSBnZXQgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFdlIHVzZWQgdGhlIHF1YXNpLWxpa2VsaWhvb2QgbW9kZWxzIHNpbmNlIG91ciBkYXRhc2V0IGlzIGZyb20gYW4gUk5BU2VxIGV4cGVyaW1lbnQgYW5kIHF1YXNpLWxpa2VsaWhvb2QgbW9kZWxzIGFyZSBiZXN0IHN1aXRlZCB0byBoYW5kbGUgUk5BU2VxIGRhdGEuCgpgYGB7ciBmaXRfcWwsIG1lc3NhZ2U9RkFMU0V9CmZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZCwgbW9kZWxfZGVzaWduKQpgYGAKCk9uY2Ugd2UgaGF2ZSBmaXQgdGhlIG1vZGVsLCB3ZSBjYW4gcHJvY2VlZCB0byBjYWxjdWxhdGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIFJlY2FsbCB0aGF0IG91ciBnb2FsIGlzIHRvIHZlcmlmeSB0aGUgcm9sZSBvZiBjYW5uYWJpZGlvbCBpbiBhZmZlY3RpbmcgdGhlIHJlcGxpY2F0aW9uIGFiaWxpdHkgb2YgU0FSUy1Db1YtMiwgc28gd2Ugd2lsbCBiZSB1c2luZyBgY2JkX3RyZWF0bWVudGAgYXMgdGhlIGNvbnRyYXN0LgoKYGBge3IgdGVzdF9xbCwgbWVzc2FnZT1GQUxTRX0KcWxmIDwtIGVkZ2VSOjpnbG1RTEZUZXN0KGZpdCwgY29lZiA9ICdzYW1wbGVzJGNiZF90cmVhdG1lbnRDQkQnKQpxbGZfZGlmZl9leHAgPC0gZWRnZVI6OnRvcFRhZ3MocWxmLCBzb3J0LmJ5ID0gIlBWYWx1ZSIsIG4gPSBucm93KG5vcm1hbGl6ZWRfY291bnRzX2Fubm90KSkKa25pdHI6OmthYmxlKHFsZl9kaWZmX2V4cFsxOjEwLF0kdGFibGUsIHR5cGU9Imh0bWwiLCBkaWdpdHMgPSAyMCwgY2FwdGlvbiA9ICJUb3AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIikgJT4lCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYAoKV2UgY2FuIGV4YW1pbmUgdGhlIG51bWJlciBvZiBnZW5lcyBwYXNzIHRoZSB0aHJlc2hvbGQgYW5kIGNvcnJlY3Rpb24uIFdlIGFyZSB1c2luZyAwLjA1IGFzIHRoZSB0aHJlc2hvbGQgZm9yIHAtdmFsdWUgYXMgaXQgaXMgY29tbW9ubHkgdXNlZCBpbiBwcmFjdGljZS4KCmBgYHtyIGNvdW50X2RpZmZfcHZhbCwgbWVzc2FnZT1GQUxTRX0KIyBudW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzZWQgdGhlIHRocmVzaG9sZApzdW0ocWxmX2RpZmZfZXhwJHRhYmxlJFBWYWx1ZSA8IDAuMDUpCmBgYApgYGB7ciBjb3VudF9kaWZmX2ZkciwgbWVzc2FnZT1GQUxTRX0KIyBudW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzZWQgY29ycmVjdGlvbgpzdW0ocWxmX2RpZmZfZXhwJHRhYmxlJEZEUiA8IDAuMDUpCmBgYAoKVGhlIHRocmVzaG9sZCBvZiAwLjA1IGdpdmVzIHVzIHF1aXRlIGEgbG90IG9mIGdlbmVzLCBpbiBvcmRlciB0byBnZXQgbW9yZSBtZWFuaW5nZnVsIGhpdHMgaW4gdGhlIGRvd25zdHJlYW0gZ2VuZSBlbnJpY2htZW50IGFuYWx5c2lzLCB3ZSB3aWxsIG1ha2UgdGhlIHRoZXJzaG9sZCBtb3JlIHN0cmluZ2VudC4KCmBgYHtyIGNvdW50X2RpZmZfcHZhbF9zdHJpbmdlbnQsIG1lc3NhZ2U9RkFMU0V9CiMgbnVtYmVyIG9mIGdlbmVzIHRoYXQgcGFzc2VkIHRoZSB0aHJlc2hvbGQKc3VtKHFsZl9kaWZmX2V4cCR0YWJsZSRQVmFsdWUgPCAwLjAxICYgYWJzKHFsZl9kaWZmX2V4cCR0YWJsZSRsb2dGQykgPiAxLjUpCmBgYApgYGB7ciBjb3VudF9mZHJfc3RyaW5nZW50LCBtZXNzYWdlPUZBTFNFfQojIG51bWJlciBvZiBnZW5lcyB0aGF0IHBhc3NlZCBjb3JyZWN0aW9uCnN1bShxbGZfZGlmZl9leHAkdGFibGUkRkRSIDwgMC4wMSAmIGFicyhxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMpID4gMS41KQpgYGAKCiMjIFZpc3VhbGl6YXRpb24gb2YgRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzCgpOb3cgd2UgY2FuIHJldHJpZXZlIHRoZSBsaXN0IG9mIGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMgYW5kIHZpc3VhbGl6ZSB1c2luZyBkaWZmZXJlbnQgcGxvdHMuIFdlIHdpbGwgZmlyc3QgcGxvdCB0aGVtIG9uIGEgdm9sY2FubyBwbG90LiBFYWNoIGdlbmUgaXMgcmVwcmVzZW50ZWQgYnkgYSBwb2ludCBpbiB0aGUgcGxvdC4gVGhlIGhvcml6b250YWwgYXhpcyBvZiB0aGUgcGxvdCBpcyB0aGUgJFxsb2dfMiQgZm9sZCBjaGFuZ2UgYW5kIHRoZSB2ZXJ0aWNhbCBheGlzIGlzIHRoZSAkLVxsb2dfezEwfXAkIHdoaWNoIGluZGljYXRlcyB0aGUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIG9mIGVhY2ggZ2VuZSAoaG93IGxpa2VseSB0aGUgZGlmZmVyZW50aWFsIGlzIGR1ZSB0byBhY3R1YWwgYmlvbG9naWNhbCB2YXJpYXRpb24pLgoKYGBge3Igdm9sY2Fub19wbG90LCBtZXNzYWdlPUZBTFNFfQp2b2xjYW5vX2NvbG9yX3BhbGV0dGUgPSByZXAoJ2dyYXknLCB0aW1lcyA9IG5yb3cocWxmX2RpZmZfZXhwJHRhYmxlKSkKdm9sY2Fub19jb2xvcl9wYWxldHRlW3FsZl9kaWZmX2V4cCR0YWJsZSRsb2dGQyA8IDAgJiBxbGZfZGlmZl9leHAkdGFibGUkRkRSIDwgMC4wMSAmIGFicyhxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMpID4gMS41XSA8LSAnYmx1ZScKdm9sY2Fub19jb2xvcl9wYWxldHRlW3FsZl9kaWZmX2V4cCR0YWJsZSRsb2dGQyA+IDAgJiBxbGZfZGlmZl9leHAkdGFibGUkRkRSIDwgMC4wMSAmIGFicyhxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMpID4gMS41XSA8LSAncmVkJwoKcGxvdChxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMsIAogICAgIC1sb2cocWxmX2RpZmZfZXhwJHRhYmxlJFBWYWx1ZSwgYmFzZT0xMCksIAogICAgIGNvbCA9IHZvbGNhbm9fY29sb3JfcGFsZXR0ZSwKICAgICB4bGFiID0gImxvZzIgZm9sZCBjaGFuZ2UiLAogICAgIHlsYWIgPSAiLWxvZzEwIHAiLAogICAgIG1haW4gPSAiVm9sY2FubyBwbG90IHNob3dpbmcgdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMiCiAgICApCgpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kPWMoIkRvd25yZWd1bGF0ZWQgaW4gQ0JEIHRyZWF0ZWQgY2VsbHMiLCJVcHJlZ3VsYXRlZCBpbiBDQkQgdHJlYXRlZCBjZWxscyIsICJOb3Qgc2lnbmlmaWNhbnQiKSxmaWxsID0gYygiYmx1ZSIsICJyZWQiLCAiZ3JleSIpLCBjZXggPSAwLjUpCmBgYAoKTmV4dCwgd2Ugd2lsbCB2aXN1YWxpemUgdGhlIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzIGFjcm9zcyBkaWZmZXJlbnQgdGVzdCBjb25kaXRpb25zIHVzaW5nIGEgaGVhdG1hcC4KCmBgYHtyIGhlYXRtYXAsIG1lc3NhZ2U9RkFMU0V9CmRpZmZfZXhwX2xzdCA8LSBxbGZfZGlmZl9leHAkdGFibGVbcWxmX2RpZmZfZXhwJHRhYmxlJEZEUiA8IDAuMDEgJiBhYnMocWxmX2RpZmZfZXhwJHRhYmxlJGxvZ0ZDKSxdCmRpZmZfZXhwX2xzdCRoZ25jX3N5bWJvbCA8LSByb3duYW1lcyhkaWZmX2V4cF9sc3QpCgojIG5vcm1hbGl6ZWQgY291bnRzIG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwptYXRfY291bnRfbm9ybWFsaXplZF9kaWZmIDwtIG5vcm1hbGl6ZWRfY291bnRzX2Fubm90W2RpZmZfZXhwX2xzdCRoZ25jX3N5bWJvbCwgMzpuY29sKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90KV0KIyByZXNjYWxlIHRoZSBjb3VudHMKbWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZiA8LSB0KHNjYWxlKHQobWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZikpKQpoZWF0bWFwX2NvbG9yX3BhbGV0dGUgPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYyhtaW4obWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZiksIDAsIG1heChtYXRfY291bnRfbm9ybWFsaXplZF9kaWZmKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChhcy5tYXRyaXgobWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZiksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAic2NhbGVkIG5vcm1hbGl6ZWQgY291bnQiLAogICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gaGVhdG1hcF9jb2xvcl9wYWxldHRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiU2FtcGxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHJvd190aXRsZSA9ICJHZW5lcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHVzZV9yYXN0ZXIgPSBUUlVFKQpgYGAKCiMjIERpc2N1c3Npb24KCjEuIEluaXRpYWxseSwgSSB1c2VkIHRoZSBwLXZhbHVlIG9mIDAuMDUgYXMgaXQgaXMgd2lkZWx5IHVzZWQgaW4gcHJhY3RpY2UuIFRoaXMgZ2l2ZXMgdXMgNTUwMSBnZW5lcyBwcmlvciB0byBjb3JyZWN0aW9uLiBUaGlzIGlzIHF1aXRlIGEgbGFyZ2UgbnVtYmVyIG9mIGdlbmVzLCBzbyB3ZSBjaGFuZ2VkIHRoZSBwLXZhbHVlIHRocmVzaG9sZCB0byAwLjAxIHRvIGxpbWl0IHRoZSBudW1iZXIgb2YgZ2VuZXMgaW5jbHVkZWQuIFdlIGZ1cnRoZXIgYWRkZWQgdGhlIGNyaXRlcmlhIHRoYXQgYSBnZW5lIG11c3QgaGF2ZSBhbiBhYnNvbHV0ZSBsb2cgZm9sZCBjaGFuZ2UgZ3JlYXRlciB0aGFuIDEuNS4gQnkgZG9pbmcgc28sIHdlIGFyZSBjb250cmFpbmluZyBvdXJzZWx2ZXMgdG8gb25seSBnZXQgdGhlIGdlbmVzIHRoYXQgYXJlIGhpZ2hseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgd2l0aCBoaWdoIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZS4KCjIuIEZvciBjb3JyZWN0aW9uLCB3ZSB1c2VkIEJlbmphbWluaS1Ib2NoYmVyZy4gVGhlIHR3byBtYWluIG1ldGhvZHMgZGlzY3Vzc2VkIGZvciBjb3JyZWN0aW5nIGZhbWlseS13aXNlIGZhbHNlIGRpc2NvdmVyeSByYXRlIGFyZSBCb25mZXJyb25pIGFuZCBCZW5qYW1pbmktSG9jaGJlcmcgY29ycmVjdGlvbi4gV2Ugd2FudCB0byBnZXQgbWVhbmluZ2Z1bCBoaXRzIHdpdGhvdXQgZXhjbHVkaW5nIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgb25lcy4gQm9uZmVycm9uaSdzIG1ldGhvZCBpcyBvdmVybHkgc3RyaW5nZW50IHNvIGl0IGlzIG5vdCBxdWl0ZSBzdWl0YWJsZSBmb3Igb3VyIHB1cnBvc2VzLiBVc2luZyBCZW5qYW1pbmktSG9jaGJlcmcgd2lsbCBnaXZlIHVzIGEgcmljaGVyIHNldCBvZiBnZW5lcyB0aGF0IHdlIGNhbiB1c2UgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCgozLiBWb2xjYW5vIHBsb3Qgc2hvd24gYWJvdmUuIE5vdGUgdGhhdCB3ZSBhbHNvIGFwcGxpZWQgdGhlIHJlc3RyaWN0aW9uIHRoYXQgb25seSBnZW5lcyB3aXRoIGFib3NsdXRlIGxvZyBmb2xkIGNoYW5nZSBncmVhdGVyIHRoYW4gMS41IGFyZSB0byBiZSBpbmNsdWRlZC4KCjQuIFRoZXJlIGlzIHNpZ25pZmljYW50IGNsdXN0ZXJpbmcgd2l0aGluIGNvbmRpdGlvbnMuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgdGVzdCBjb25kaXRpb24gKENCRCB2LnMuIG5vIENCRCkgZG9lcyBoYXZlIGEgc2lnbmlmaWNhbnQgZWZmZWN0IG9uIGdlbmUgZXhwcmVzc2lvbnMuCgojIFRocmVzaG9sZGVkIE92ZXJyZXByZXNlbnRhdGlvbiBBbmFseXNpcyB1c2luZyBnOlByb2ZpbGVyCgpGb3IgdGhlIGZpbmFsIHBhcnQgb2YgdGhpcyBhc3NpZ25tZW50LCB3ZSB3aWxsIHBlcmZvcm0gYSB0aHJlc2hvbGRlZCBvdmVycmVwcmVzZW50YXRpb24gYW5hbHlzaXMgdXNpbmcgZzpQcm9maWxlci4gSW4gdGhlIHByZXZpb3VzIHNlY3Rpb24sIHdlIGhhdmUgY29tcGlsZWQgYSBsaXN0IG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcy4gSGVyZSwgd2Ugd2FudCB0byBmdXJ0aGVyIGRpdmlkZSB0aGVtIGludG8gdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMuCgpgYGB7ciBzcGxpdF91cGRvd25yZWd1bGF0ZWR9CnVwcmVndWxhdGVkX2dlbmVfbHN0IDwtIGRpZmZfZXhwX2xzdFtkaWZmX2V4cF9sc3QkbG9nRkMgPiAwLF0KZG93bnJlZ3VsYXRlZF9nZW5lX2xzdCA8LSBkaWZmX2V4cF9sc3RbZGlmZl9leHBfbHN0JGxvZ0ZDIDwgMCxdCmBgYAoKV2UgdXNlIHRoZSBSIHBhY2thZ2UgZm9yIGc6UHJvZmlsZXIgdG8gcGVyZm9ybSB0aGUgZ2VuZSBlbnJpY2htZW50IGFuYWx5c2lzLiBGb3IgY29ycmVjdGlvbiwgd2UgdXNlZCBGRFIgYXMgaXQgaXMgbGVzcyBzdHJpbmdlbnQgdGhhbiBCb25mZXJyb25pIGFuZCBpcyBpbnRyb2R1Y2VkIGFzIHRoZSBwcmVmZXJyZWQgY29ycmVjdGlvbiBtZXRob2QgaW4gY2xhc3MuIFdlIHVzZWQgR08gQmlvbG9naWNhbCBQcm9jZXNzLCBHTyBNb2xlY3VsYXIgRnVuY3Rpb24sIGFuZCBLRUdHIGFzIHRob3NlIGFyZSB0aGUgb25lcyB1c2VkIGJ5IHRoZSBhdXRob3Igb2YgdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uLiBGb3IgYSBtb3JlIGRldGFpbGVkIG92ZXJ2aWV3IG9mIHRoZSB3b3JrZmxvdywgcGxlYXNlIHJlZmVyIHRvIHRoZSBEaXNjdXNzaW9uIHN1YnNlY3Rpb24gbG9jYXRlZCBhdCB0aGUgZW5kIG9mIHRoaXMgc2VjdGlvbi4KCiMjIFVwcmVndWxhdGVkIEdlbmVzCgpgYGB7ciBncHJvZmlsZXJfdXAsIG1lc3NhZ2U9RkFMU0V9CnVwX3RvcF90ZXJtc19hbGwgPC0gZ3Byb2ZpbGVyMjo6Z29zdChxdWVyeSA9IHJvd25hbWVzKHVwcmVndWxhdGVkX2dlbmVfbHN0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJoc2FwaWVucyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjbHVkZV9pZWEgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZXMgPSBjKCJHTzpCUCIsICJHTzpNRiIsICJLRUdHIikpCgp1cF90b3BfdGVybXMgPC0gZGF0YS5mcmFtZSgKICB0ZXJtX25hbWUgPSB1cF90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX25hbWVbdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplIDwgNTAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cF90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPiAyXSwKICB0ZXJtX2lkID0gdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9pZFt1cF90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPCA1MDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0sCiAgc291cmNlID0gdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkc291cmNlW3VwX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA8IDUwMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0KKQoKa25pdHI6OmthYmxlKHVwX3RvcF90ZXJtc1sxOjEwLF0sIGNhcHRpb24gPSAiVG9wIGdlbmVzZXRzIHVzaW5nIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMiKSAlPiUga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYApGb3IgY29udGV4dCwgbGV0J3MgZXhhbWluZSB0aGUgdG9wIHRlcm0gZnJvbSBlYWNoIGRhdGEgc291cmNlLgoKYGBge3IgdXBfdG9wX3Rlcm1fcGVyX2NhdGVnb3J5LCBtZXNzYWdlPUZBTFNFfQprbml0cjo6a2FibGUocmJpbmQodXBfdG9wX3Rlcm1zW3VwX3RvcF90ZXJtcyRzb3VyY2UgPT0gIkdPOkJQIixdWzEsXSwKICAgICAgICAgICAgICAgICAgIHVwX3RvcF90ZXJtc1t1cF90b3BfdGVybXMkc291cmNlID09ICJHTzpNRiIsXVsxLF0sCiAgICAgICAgICAgICAgICAgICB1cF90b3BfdGVybXNbdXBfdG9wX3Rlcm1zJHNvdXJjZSA9PSAiS0VHRyIsXVsxLF0pLAogICAgICAgICAgICAgY2FwdGlvbiA9ICJUb3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMiKSAlPiUKICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikKYGBgCldlIGNhbiB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0b3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGFuIE1hbmhhdHRhbiBwbG90LiBUaGlzIGlzIHRoZSBkaXN0cmlidXRpb24gb2YgdGVybXMgcHJpb3IgdG8gcmVtb3ZpbmcgbGFyZ2UgdGVybXMgd2l0aCBvdmVyIDUwMCBnZW5lcy4KCmBgYHtyIHVwX2Rpc3RfcGxvdH0KZ3Byb2ZpbGVyMjo6Z29zdHBsb3QodXBfdG9wX3Rlcm1zX2FsbCkgJT4lIHBsb3RseTo6bGF5b3V0KHRpdGxlID0gIk1hbmhhdHRhbiBwbG90IHNob3dpbmcgZGlzdHJpYnV0aW9uIG9mIHRlcm1zIFxuZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMiLCBmb250ID0gbGlzdChzaXplID0gMTApKQpgYGAKCmBgYHtyIGNvdW50X3VwX3RvcF90ZXJtcywgbWVzc2FnZT1GQUxTRX0KbGVuZ3RoKHVwX3RvcF90ZXJtcyR0ZXJtX25hbWUpCmBgYAoKIyMgRG93bnJlZ3VsYXRlZCBHZW5lcwoKV2UgZG8gdGhlIHNhbWUgZm9yIHRoZSBkb3ducmVndWFsdGVkIGdlbmVzLgoKYGBge3IgZ3Byb2ZpbGVyX2Rvd24sIG1lc3NhZ2U9RkFMU0V9CmRvd25fdG9wX3Rlcm1zX2FsbCA8LSBncHJvZmlsZXIyOjpnb3N0KHF1ZXJ5ID0gcm93bmFtZXMoZG93bnJlZ3VsYXRlZF9nZW5lX2xzdCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAiaHNhcGllbnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2x1ZGVfaWVhID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2VzID0gYygiR086QlAiLCAiR086TUYiLCAiS0VHRyIpKQoKZG93bl90b3BfdGVybXMgPC0gZGF0YS5mcmFtZSgKICB0ZXJtX25hbWUgPSBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fbmFtZVtkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA8IDUwMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdLAogIHRlcm1faWQgPSBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1faWRbZG93bl90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPCA1MDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdLAogIHNvdXJjZSA9IGRvd25fdG9wX3Rlcm1zX2FsbCRyZXN1bHQkc291cmNlW2Rvd25fdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplIDwgNTAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvd25fdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0KKQoKa25pdHI6OmthYmxlKGRvd25fdG9wX3Rlcm1zWzE6MTAsXSwgY2FwdGlvbiA9ICJUb3AgZ2VuZXNldHMgdXNpbmcgbGlzdCBvZiBkb3ducmVndWxhdGVkIGdlbmVzIikgJT4lIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoInN0cmlwZWQiKQpgYGAKCmBgYHtyIGRvd25fdG9wX3Rlcm1fcGVyX2NhdGVnb3J5LCBtZXNzYWdlPUZBTFNFfQprbml0cjo6a2FibGUocmJpbmQoZG93bl90b3BfdGVybXNbZG93bl90b3BfdGVybXMkc291cmNlID09ICJHTzpCUCIsXVsxLF0sCiAgICAgICAgICAgICAgICAgICBkb3duX3RvcF90ZXJtc1tkb3duX3RvcF90ZXJtcyRzb3VyY2UgPT0gIkdPOk1GIixdWzEsXSwKICAgICAgICAgICAgICAgICAgIGRvd25fdG9wX3Rlcm1zW2Rvd25fdG9wX3Rlcm1zJHNvdXJjZSA9PSAiS0VHRyIsXVsxLF0pLAogICAgICAgICAgICAgY2FwdGlvbiA9ICJUb3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyIpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoInN0cmlwZWQiKQpgYGAKUGxvdCB0aGUgTWFuaGF0dGFuIHBsb3Qgc2hvd2luZyBkaXN0cmlidXRpb24gb2YgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcy4KCmBgYHtyIGRvd25fZGlzdF9wbG90fQpncHJvZmlsZXIyOjpnb3N0cGxvdChkb3duX3RvcF90ZXJtc19hbGwpICU+JSBwbG90bHk6OmxheW91dCh0aXRsZSA9ICJNYW5oYXR0YW4gcGxvdCBzaG93aW5nIGRpc3RyaWJ1dGlvbiBvZiB0ZXJtc1xuIGZyb20gZWFjaCBkYXRhIHNvdXJjZSB1c2luZyBsaXN0IG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMiLCBmb250ID0gbGlzdChzaXplID0gMTApKQpgYGAKCmBgYHtyIGNvdW50X2Rvd25fdG9wX3Rlcm1zLCBtZXNzYWdlPUZBTFNFfQpsZW5ndGgoZG93bl90b3BfdGVybXMkdGVybV9uYW1lKQpgYGAKCiMjIEFsbCBEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMKCkZpbmFsbHksIGZvciBhbGwgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLgoKYGBge3IgZ3Byb2ZpbGVyX292ZXJhbGwsIG1lc3NhZ2U9RkFMU0V9CnRvcF90ZXJtc19hbGwgPC0gZ3Byb2ZpbGVyMjo6Z29zdChxdWVyeSA9IHJvd25hbWVzKGRpZmZfZXhwX2xzdCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAiaHNhcGllbnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2x1ZGVfaWVhID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2VzID0gYygiR086QlAiLCAiR086TUYiLCAiS0VHRyIpKQoKdG9wX3Rlcm1zIDwtIGRhdGEuZnJhbWUoCiAgdGVybV9uYW1lID0gdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9uYW1lW3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA8IDUwMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0sCiAgdGVybV9pZCA9IHRvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1faWRbdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplIDwgNTAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdLAogIHNvdXJjZSA9IHRvcF90ZXJtc19hbGwkcmVzdWx0JHNvdXJjZVt0b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPCA1MDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdCikKCmtuaXRyOjprYWJsZSh0b3BfdGVybXNbMToxMCxdLCBjYXB0aW9uID0gIlRvcCBnZW5lc2V0cyB1c2luZyBsaXN0IG9mIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMiKSAlPiUga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYAoKYGBge3IgdG9wX3Rlcm1fcGVyX2NhdGVnb3J5LCBtZXNzYWdlPUZBTFNFfQprbml0cjo6a2FibGUocmJpbmQodG9wX3Rlcm1zW3RvcF90ZXJtcyRzb3VyY2UgPT0gIkdPOkJQIixdWzEsXSwKICAgICAgICAgICAgICAgICAgIHRvcF90ZXJtc1t0b3BfdGVybXMkc291cmNlID09ICJHTzpNRiIsXVsxLF0sCiAgICAgICAgICAgICAgICAgICB0b3BfdGVybXNbdG9wX3Rlcm1zJHNvdXJjZSA9PSAiS0VHRyIsXVsxLF0pLAogICAgICAgICAgICAgY2FwdGlvbiA9ICJUb3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgYWxsIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoInN0cmlwZWQiKQpgYGAKYGBge3IgYWxsX2Rpc3RfcGxvdH0KZ3Byb2ZpbGVyMjo6Z29zdHBsb3QodG9wX3Rlcm1zX2FsbCkgJT4lIHBsb3RseTo6bGF5b3V0KHRpdGxlID0gIk1hbmhhdHRhbiBwbG90IHNob3dpbmcgZGlzdHJpYnV0aW9uIG9mIHRlcm1zIGZyb20gZWFjaCBkYXRhIHNvdXJjZSIsIGZvbnQgPSBsaXN0KHNpemUgPSAxMCkpCmBgYAoKYGBge3IgY291bnRfdG9wX3Rlcm1zLCBtZXNzYWdlPUZBTFNFfQpsZW5ndGgodG9wX3Rlcm1zJHRlcm1fbmFtZSkKYGBgCgojIyBEaXNjdXNzaW9uCgoxLiBXZSB1c2VkIGc6UHJvZmlsZXIgYXMgd2UgaGF2ZSBleHRlbnNpdmVseSBkaXNjdXNzZWQgYWJvdXQgaXQgaW4gY2xhc3MuIEl0IGhhcyBhIG5pY2Ugd2ViLWJhc2VkIGludGVyZmFjZSBhcyB3ZWxsIGFzIGVhc3ktdG8tdXNlIEFQSXMgaW4gdGhlIGZvcm0gb2YgYW4gUiBwYWNrYWdlLiBGdXJ0aGVybW9yZSwgdGhlIGRhdGEgc291cmNlcyBvbiBnOlByb2ZpbGVyIGlzIGZyZXF1ZW50bHkgdXBkYXRlZCBhbmQgaW5jbHVkZSB0aGUgZGF0YSBzb3VyY2VzIHRoYXQgd2UgYXJlIG1vc3QgaW50ZXJlc3RlZCBpbi4KCjIuIFdlIHVzZWQgR08gQmlvbG9naWNhbCBQcm9jZXNzLCBHTyBNb2xlY3VsYXIgRnVuY3Rpb24sIGFuZCBLRUdPLiBXZSBjaG9zZSB0aGVzZSBkYXRhIHNvdXJjZXMgc2luY2UgdGhleSBhcmUgYWxzbyB1c2VkIGJ5IHRoZSBhdXRob3Igb2YgdGhlIG9yaWdpbmFsIHBhcGVyIGZyb20gd2hpY2ggSSBvYnRhaW5lZCB0aGUgZGF0YXNldC4gSG93ZXZlciwgc2luY2UgS0VHTyBpcyBhIGNvbW1lcmNpYWwgZGF0YSBzb3VyY2UsIGlmIGl0IHdhcyBub3QgZm9yIHRoYXQgZmFjdCB0aGF0IHRoZSBvcmlnaW5hbCBwYXBlciB1c2VkIGl0LCBJIHdvdWxkIHBlcnNvbmFsbHkgcmVmcmFpbiBmcm9tIHVzaW5nIGl0LiBUaGUgb3JpZ2luYWwgYXV0aG9yIGFsc28gdXNlZCBvdGhlciBkYXRhIHNvdXJjZXMgaW5jbHVkaW5nIENhbm9uaWNhbCBQYXRod2F5cyBidXQgc2luY2UgaXQgaXMgbm90IHBhcnQgb2YgZzpQcm9maWxlciwgd2UgZGlkIG5vdCBpbmNsdWRlIHRoZXNlIGRhdGEgc291cmNlcy4gVGhlIGFubm90YXRpb24gc291cmNlIHZlcnNpb25zIGFyZSBhcyBmb2xsb3dzOiBFbnNlbWJsIDEwNSwgRW5zZW1ibCBHZW5vbWVzIDUyIChkYXRhYmFzZSBidWlsdCBvbiAyMDIyLTAyLTE0KQoKMy4gRm9yIGFsbCB0aHJlZSBhbmFseXNpcyAodXNpbmcgdXByZWd1bGF0ZWQsIGRvd25yZWd1bGF0ZWQsIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQpLCB3ZSB1c2VkIGEgdGhyZXNob2xkIGJldHdlZW4gMiBhbmQgNTAwLiBXZSBzZXQgdGhlIHVwcGVyIGJvdW5kIHRvIDUwMCBiZWNhdXNlIHdlIGRvIG5vdCB3YW50IHRvIGluY2x1ZGUgb3Zlcmx5IGJyb2FkIGFuZCBnZW5lcmljIHRlcm1zIHRoYXQgd2lsbCBub3QgZ2l2ZSB1cyBtZWFuaW5nZnVsIGluc2lnaHRzIGludG8gdGhlIHJvbGVzIG9mIHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFRoZSBzZXQgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgcmV0dXJuZWQgNDMzIGdlbmVzZXRzOyB0aGUgc2V0IG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgcmV0dXJuZWQgNTYwIGdlbmVzZXRzOyB0aGUgc2V0IG9mIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgcmV0dXJuZWQgNjE2IGdlbmUgc2V0cy4KCjQuIFRoZSByZXN1bHQgdXNpbmcgdGhlIHdob2xlIGxpc3QgKHNldCBvZiBhbGwgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzKSBpcyBtb3JlIHByZWRvbWluYW50bHkgcmVwcmVzZW50ZWQgYnkgdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMuIEl0IGFsc28gZG9lcyBub3QgcHJvdmlkZSBhIGxvdCBvZiBpbnNpZ2h0cyBpbnRvIHRoZSByb2xlcyBvZiB0aGUgZ2VuZXMgYXMgaXQgaXMgYSBtaXggb2YgZHJhc3RpY2FsbHkgZGlmZmVyZW50IGFuZCBzZWVtaW5nbHkgdW5yZWxhdGVkIHRlcm1zIChlLmcuIG1pdG90aWMgbnVjbGVhciBkaXZpc2lvbiwgb3JnYW5lbGxlIGZpc3Npb24sIHJlc3BvbnNlIHRvIGVuZG9wbGFzbWljIHJldGljdWx1bSBzdHJlc3MsIGV0Yy4pLiBIb3dldmVyLCBieSB1c2luZyBzZXBhcmF0aW5nIGludG8gdXByZWd1YWx0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMgYW5kIHBlcmZvcm1pbmcgdGhlIGdlbmUgZW5yaWNobWVudCBhbmFseXNpcyBzZXBhcmF0ZWx5LCB3ZSBnZXQgbW9yZSBtZWFuaW5nZnVsIHRlcm1zOiB0aGUgdXByZWd1YWx0ZWQgZ2VuZXMgYXJlIG1vc3RseSBhc3NvY2lhdGVkIHdpdGggcGF0aHdheXMgYW5kIHByb2Nlc3NlcyBpbnZvbHZpbmcgZW5kb3BsYXNtaWMgcmV0aWN1bHVtIHdoZXJlYXMgdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMgYXJlIG1vc3RseSBhc3NvY2lhdGVkIHdpdGggdGhlIGNlbGwgY3ljbGUuCgoKIyBJbnRlcnByZXRhdGlvbgoKMS4gWWVzLiBUaGUgb3ZlcnJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIHJlc3VsdHMgc3VwcG9ydCB0aGUgY29uY2x1c2lvbiBkaXNjdXNzZWQgaW4gdGhlIG9yaWdpbmFsIHBhcGVyLiBUaGUgb3JpZ2luYWwgcGFwZXIgY2xhaW1zIHRoYXQgQ0JEIHRyZWF0bWVudCBjYW4gaW5oaWJpdCBTQVJTLUNvVi0yIHJlcGxpY2F0aW9uIHRocm91Z2ggdGhlIGhvc3QncyBFUiBzdHJlc3MgcmVzcG9uc2UgcGF0aHdheS4gQWx0aG91Z2ggd2Ugd2VyZSBub3QgYWJsZSB0byB2ZXJpZnkgd2hldGhlciBvciBub3QgdGhpcyBoYXMgYW55IGFmZmVjdCBvbiBTQVJTLUNvVi0yIHJlcGxpY2F0aW9uLCB3ZSBhcmUgYWJsZSB0byBjb25maXJtIGJhc2VkIG9uIHRoZSB0b3AgdGVybXMgcmV0dXJuZWQgYnkgdGhlIG92ZXJyZXByZXNlbnRhdGlvbiBhbmFseXNpcyB0aGF0IHRoZSBnZW5lcyBhc3NvY2lhdGVkIHdpdGggRVIgc3RyZXNzIHJlc3BvbnNlIGFyZSBpbmRlZWQgdXByZWd1bGF0ZWQgaW4gdGhlIHNhbXBsZXMgdHJlYXRlZCB3aXRoIENCRC4KCjIuIFRoZSBjb25jbHVzaW9uIHRoYXQgQ0JEIGlzIGNvcnJlbGF0ZWQgdG8gcmVkdWNlZCByaXNrIG9yIHNldmVyaXR5IG9mIFNBUlMtQ29WLTIgaXMgc3VwcG9ydGVkIGJ5IHRlc3RpbmcgYWR1bHQgcGF0aWVudHMgYXMgcHJlc2VudGVkIGJ5IHRoZSBvcmlnaW5hbCBwYXBlci4gVGhlcmUgYXJlIGFsc28gb3RoZXIgcGFwZXJzIHdobyBkaXNjdXNzZWQgdGhlIHJvbGUgb2YgQ0JEIGluIFNBUlMtQ29WLTIgaW5mZWN0aW9uICh2YW4gQnJlZW1hbiBldCBhbC4pLiBUaGUgcmVzdWx0IGZyb20gb3VyIG92ZXJyZXByZXNlbnRhdGlvbiBhbmFseXNpcyBpcyBzdXBwb3J0ZWQgYnkgdGhlIG9yaWdpbmFsIHBhcGVyLCBpbiB3aGljaCB0aGUgYXV0aG9yIGhhcyBhbHNvIHBlcmZvcm1lZCBnZW5lIGVucmljaG1lbnQgYW5hbHlzaXMgd2l0aCBzaW1pbGFyIG91dGNvbWVzLiBIb3dldmVyLCBzaW5jZSB0aGlzIGlzIHN0aWxsIGFuIGVhcmx5IHJlc2VhcmNoIGluIHRoaXMgdG9waWMgYW5kIFNBUlMtQ29WLTIgY29udGludWVzIHRvIGV2b2x2ZSwgdGhlcmUgZG9lcyBub3Qgc2VlbSB0byBiZSBvdGhlciB1bnJlbGF0ZWQgZ3JvdXBzIHdobyBjb3VsZCBwcm92aWRlIGFkZGl0aW9uYWwgc3VwcG9ydGluZyBldmlkZW5jZSB0byBvdXIgcmVzdWx0cyBmcm9tIHRoZSBvdmVycmVwcmVzZW50YXRpb24gYW5hbHlzaXMgYmVzaWRlcyB0aGUgb3JpZ2luYWwgcGFwZXIuCgojIEpvdXJuYWwKCkxpbmsgdG8gbXkgam91cm5hbCBlbnRyeSBmb3IgdGhpcyBhc3NpZ25tZW50OiBodHRwczovL2dpdGh1Yi5jb20vYmNiNDIwLTIwMjIvS2V2aW5fR2FvL3dpa2kvQXNzaWdubWVudC0yCgojIFJlZmVyZW5jZXMKCi0gQ2hlbiBZLCBMdW4gQVRMLCBTbXl0aCBHSyAoMjAxNikuIEZyb20gcmVhZHMgdG8gZ2VuZXMgdG8gcGF0aHdheXM6IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9mIFJOQS1TZXEgZXhwZXJpbWVudHMgdXNpbmcgUnN1YnJlYWQgYW5kIHRoZQogIGVkZ2VSIHF1YXNpLWxpa2VsaWhvb2QgcGlwZWxpbmUuIEYxMDAwUmVzZWFyY2ggNSwgMTQzOAotIER1cmluY2ssIFMuLCBTcGVsbG1hbiwgUC4gVC4sIEJpcm5leSwgRS4sICYgSHViZXIsIFcuICgyMDA5KS4gTWFwcGluZyBpZGVudGlmaWVycyBmb3IgdGhlIGludGVncmF0aW9uIG9mIGdlbm9taWMgZGF0YXNldHMgd2l0aCB0aGUgUi9CaW9jb25kdWN0b3IgcGFja2FnZSBiaW9tYVJ0LiBOYXR1cmUgLSBwcm90b2NvbHMsIDQoOCksIDExODTigJMxMTkxLiBodHRwczovL2RvaS5vcmcvMTAuMTAzOC9ucHJvdC4yMDA5Ljk3Ci0gTWFydGluIE1vcmdhbiAoMjAyMSkuIEJpb2NNYW5hZ2VyOiBBY2Nlc3MgdGhlIEJpb2NvbmR1Y3RvciBQcm9qZWN0IFBhY2thZ2UgUmVwb3NpdG9yeS4gUiBwYWNrYWdlIHZlcnNpb24gMS4zMC4xNi4KICBodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPUJpb2NNYW5hZ2VyCi0gTWNDYXJ0aHkgREosIENoZW4gWSBhbmQgU215dGggR0sgKDIwMTIpLiBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvZiBtdWx0aWZhY3RvciBSTkEtU2VxIGV4cGVyaW1lbnRzIHdpdGggcmVzcGVjdCB0byBiaW9sb2dpY2FsCiAgdmFyaWF0aW9uLiBOdWNsZWljIEFjaWRzIFJlc2VhcmNoIDQwLCA0Mjg4LTQyOTcKLSBOZ3V5ZW4sIEwuIEMuLCBZYW5nLCBELiwgTmljb2xhZXNjdSwgVi4sIEJlc3QsIFQuIEouLCBPaHRzdWtpLCBULiwgQ2hlbiwgUy4tTi4sIEZyaWVzZW4sIEouIEIuLCBEcmF5bWFuLCBOLiwgTW9oYW1lZCwgQS4sIERhbm4sIEMuLCBTaWx2YSwgRC4sIEd1bGEsIEguLCBKb25lcywgSy4gQS4sIE1pbGxpcywgSi4gTS4sIERpY2tpbnNvbiwgQi4gQy4sIFRheSwgUy4sIE9ha2VzLCBTLiBBLiwgUGF1bGksIEcuIEYuLCBNZWx0emVyLCBELiBPLiwg4oCmIFJvc25lciwgTS4gUi4gKDIwMjEpLiBDYW5uYWJpZGlvbCBpbmhpYml0cyBTQVJTLUNPVi0yIHJlcGxpY2F0aW9uIGFuZCBwcm9tb3RlcyB0aGUgaG9zdCBpbm5hdGUgaW1tdW5lIHJlc3BvbnNlLiBTY2llbmNlIEFkdmFuY2VzLiBodHRwczovL3d3dy5zY2llbmNlLm9yZy9kb2kvYWJzLzEwLjExMjYvc2NpYWR2LmFiaTYxMTAKLSBSb2JpbnNvbiBNRCwgTWNDYXJ0aHkgREogYW5kIFNteXRoIEdLICgyMDEwKS4gZWRnZVI6IGEgQmlvY29uZHVjdG9yIHBhY2thZ2UgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9mIGRpZ2l0YWwgZ2VuZSBleHByZXNzaW9uIGRhdGEuCiAgQmlvaW5mb3JtYXRpY3MgMjYsIDEzOS0xNDAKLSBHdSwgWi4gKDIwMTYpIENvbXBsZXggaGVhdG1hcHMgcmV2ZWFsIHBhdHRlcm5zIGFuZCBjb3JyZWxhdGlvbnMgaW4gbXVsdGlkaW1lbnNpb25hbCBnZW5vbWljIGRhdGEuIEJpb2luZm9ybWF0aWNzLgotIEtvbGJlcmcgTCwgUmF1ZHZlcmUgVSwgS3V6bWluIEksIFZpbG8gSiwgUGV0ZXJzb24gSCAoMjAyMCkuIOKAnGdwcm9maWxlcjItIGFuIFIgcGFja2FnZSBmb3IgZ2VuZSBsaXN0IGZ1bmN0aW9uYWwgZW5yaWNobWVudAphbmFseXNpcyBhbmQgbmFtZXNwYWNlIGNvbnZlcnNpb24gdG9vbHNldCBnOlByb2ZpbGVyLuKAnSBfRjEwMDBSZXNlYXJjaF8sICo5IChFTElYSVIpKig3MDkpLiBSIHBhY2thZ2UgdmVyc2lvbiAwLjIuMS4KLSBTdGVmYW4gTWlsdG9uIEJhY2hlIGFuZCBIYWRsZXkgV2lja2hhbSAoMjAyMCkuIG1hZ3JpdHRyOiBBIEZvcndhcmQtUGlwZSBPcGVyYXRvciBmb3IgUi4gaHR0cHM6Ly9tYWdyaXR0ci50aWR5dmVyc2Uub3JnLAogIGh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvbWFncml0dHIuCi0gdmFuIEJyZWVtZW4sIFIuIEIuLCBNdWNoaXJpLCBSLiBOLiwgQmF0ZXMsIFQuIEEuLCBXZWluc3RlaW4sIEouIEIuLCBMZWllciwgSC4gQy4sIEZhcmxleSwgUy4sICYgVGFmZXNzZSwgRi4gRy4gKDIwMjIpLiBDYW5uYWJpbm9pZHMgQmxvY2sgQ2VsbHVsYXIgRW50cnkgb2YgU0FSUy1Db1YtMiBhbmQgdGhlIEVtZXJnaW5nIFZhcmlhbnRzLiBKb3VybmFsIG9mIG5hdHVyYWwgcHJvZHVjdHMsIDg1KDEpLCAxNzbigJMxODQuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDIxL2Fjcy5qbmF0cHJvZC4xYzAwOTQ2